iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0
Modern Web

前後端整合,用Spring boot 與React 開發屬於自己的記帳網頁系列 第 28

Day29 Spring boot security:後端登入API開發

  • 分享至 

  • xImage
  •  

前言

不知不覺已經來到了最後的兩天,在Spring Security的部分除了設置權限、指定帳密之外,我們也可以從資料庫中取得帳號密碼,以及對應的權限,而且要增加保密的程度,JWT的方式也是一種強化。但因為時間有限,這些部份未來有機會再跟大家分享。
我們的目的是能夠設置一個網站,所以Security設定好之後,我們也需要去前端去新增登入的方法,才能完整的完成前後端整合
所以今天,是要來教大家從後端開發登入的API,最後一天,則是教大家從前端去呼叫這個登入的API
那就讓我們開始吧!

登入Entity設置

我們要提供API,就要告訴前端我們要輸入的資料型態,所以我們要先創建登入的資料型態,在dto的資料夾中,新增一個LoginDto.java這個資料夾
https://ithelp.ithome.com.tw/upload/images/20241003/20152864ncQ0JlnVnx.png
裡面的程式碼如下,重點就是提供String型態的username跟Password。

package net.Eric.accounting.dto;




import lombok.AllArgsConstructor;

import lombok.Getter;

import lombok.NoArgsConstructor;

import lombok.Setter;




@Getter

@Setter

@NoArgsConstructor

@AllArgsConstructor

public class LoginDto {




	private String username;

	private String password;

	

	public LoginDto() {

		

	}

	

	public LoginDto(String username,String password) {

		this.username = username;

		this.password = password;

	}

	

	public String getUsername() {

		return username;

	}

	public void setUsername(String username) {

		this.username = username;

	}

	public String getPassword() {

		return password;

	}

	public void setPassword(String password) {

		this.password = password;

	}

}

Service創建

在Service中先創建AuthService.java
https://ithelp.ithome.com.tw/upload/images/20241003/20152864AwGqO95PUq.png
程式碼如下,我們這次只要做登入方法即可

package net.Eric.accounting.service;




import net.Eric.accounting.dto.LoginDto;




public interface AuthService {




	String login(LoginDto loginDto);

}

impl方法

這個方法是取得Config中的帳號密碼設定,然後跟登入的帳號密碼做比對,如果匹配成功,就去提供對應的權限。

package net.Eric.accounting.service.Impl;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import net.Eric.accounting.dto.LoginDto;
import net.Eric.accounting.service.AuthService;

@Service
@Component
public class AuthServiceImpl implements AuthService{

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public String login(LoginDto loginDto) {
        try {
            // 從 UserDetailsService 中獲取用戶資訊
            UserDetails userDetails = userDetailsService.loadUserByUsername(loginDto.getUsername());

            // 檢查密碼是否匹配
            if (passwordEncoder.matches(loginDto.getPassword(), userDetails.getPassword())) {
                // 如果驗證成功,可以在這裡返回成功的訊息
                return "Login successful!";
            } else {
                // 密碼不匹配
                return "Invalid username or password!";
            }
        } catch (Exception e) {
            // 如果驗證失敗,返回錯誤訊息
            return "Invalid username or password!";
        }
    }





}

完成之後我們回到Controller層,創建一個AuthController
https://ithelp.ithome.com.tw/upload/images/20241003/20152864pxoBrF8q7n.png

controller設置

程式碼會如下,只要接收帳密,並且呼叫登入的功能即可

package net.Eric.accounting.controller;




import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.CrossOrigin;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;




import net.Eric.accounting.dto.LoginDto;

import net.Eric.accounting.service.AuthService;







@CrossOrigin("*")

@RestController

@RequestMapping("/api/auth")

public class AuthController {

	

	@Autowired

	private AuthService authService;

	

	//Build Login RESt API

	@PostMapping("/login")

	public ResponseEntity<String> login(@RequestBody LoginDto loginDto){

		String response = authService.login(loginDto);

		return new ResponseEntity<>(response,HttpStatus.CREATED);

	}

}

在這裡,我們就已經創建了這個API。大家可知道所有的API都會被Security限制,而登入的API我們是開放給任何腳色使用,只有輸入正確帳密的人才會給予對應權限,所以login這個API是要permitAll的

config修改

程式碼如下

package net.Eric.accounting.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import lombok.AllArgsConstructor;

@Configuration
@AllArgsConstructor
public class SpringSecurityConfig {
	
	
	@Bean
	SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		
		http.csrf((csrf) -> csrf.disable())
			.authorizeHttpRequests((authorize)->{
				authorize.requestMatchers(HttpMethod.POST, "/api/auth/login").permitAll();
				authorize.requestMatchers(HttpMethod.POST,"/api/**").hasRole("ADMIN");
				authorize.requestMatchers(HttpMethod.GET,"/api/**").hasAnyRole("ADMIN","USER");
				
				authorize.anyRequest().authenticated();
			}).httpBasic(Customizer.withDefaults());
		

		return http.build();
	}
	
	@Bean
	public static PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	
  	@Bean
	public UserDetailsService userDetailsService() {
		UserDetails Eric = User.builder()
				.username("Eric")
				.password(passwordEncoder().encode("123321"))
				.roles("USER")
				.build();
		
		UserDetails admin = User.builder()
				.username("admin")
				.password(passwordEncoder().encode("admin"))
				.roles("ADMIN")
				.build();
		
		return new InMemoryUserDetailsManager(Eric,admin);
	}

}

修改完之後,我們可以在用POSTMAN測試,只要回傳200K即成功了!


上一篇
Day27 Spring boot Security - 設置Security Config& CSRF
下一篇
Day30 React前端登入串接與完賽心得
系列文
前後端整合,用Spring boot 與React 開發屬於自己的記帳網頁30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言